package xyz.codej.websocket.netty.server;

import io.netty.channel.Channel;
import io.netty.channel.ChannelId;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.group.ChannelGroup;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import xyz.codej.websocket.netty.server.config.NettyServerConfig;
import xyz.codej.websocket.netty.server.handler.HeartbeatHandler;
import xyz.codej.websocket.netty.server.handler.HttpRequestHandler;
import xyz.codej.websocket.netty.server.handler.TextWebSocketFrameHandler;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;


/**
 * @ClassName: WebSocketServerInitializer
 * @Description TODO
 * @Author 王华君
 * @Date 2020/4/1 3:28 下午
 * @VERSION 1.0
 */
@Component
public class WebSocketServerInitializer extends ChannelInitializer<Channel> {

    private ChannelGroup channelGroup;
    private ConcurrentHashMap<String, ChannelId> clientChannelMap;
    private final static String WS_URI = "/ws";

    @Autowired
    TextWebSocketFrameHandler textWebSocketFrameHandler;
    @Autowired
    HttpRequestHandler httpRequestHandler;
    @Autowired
    HeartbeatHandler heartbeatHandler;
    @Autowired
    NettyServerConfig serverConfig;

    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        /**
         * 五秒没有收到消息 将IdleStateHandler 添加到 ChannelPipeline 中
         */
        pipeline.addLast(new IdleStateHandler(0, 0, 5, TimeUnit.SECONDS));

        pipeline.addLast(new LoggingHandler(LogLevel.DEBUG));
        /**
         * HTTP请求的解码和编码
         * HttpServerCodec 是 HttpRequestDecoder 和 HttpResponseEncoder 的组合，可以简化 HTTP 的服务器端实现。
         */
        pipeline.addLast(new HttpServerCodec());
        pipeline.addLast(new StringEncoder());
        pipeline.addLast(new StringDecoder());
        /**
         * HttpObjectAggregator 是负责多个chunk的http 请求和响应的。
         * 他让我们的handler 处理看到的是一个完整的fullHttpResponse,
         * 不需要考虑是Content 是否是 LastHttpContent，netty的LastHttpContent代表body结束部分。
         * 一个chunk 代表一个HttpContent,最后一个chunk 由 LastHttpContent 表示
         */
        pipeline.addLast(new HttpObjectAggregator(64 * 1024));

        /**
         * 用来写文件内容
         */
        pipeline.addLast(new ChunkedWriteHandler());

        /**
         * 这个我们自定义的实现，普通HTTP请求返回HTML，WebSocket传递给
         *
         * 下一个ChannelHandler
         */
        pipeline.addLast(httpRequestHandler.setWsUri(WS_URI));

        /**
         * 我们自定义实现的处理WebSocket文本消息，转发给其他客户端
         */
        pipeline.addLast(textWebSocketFrameHandler
                .setGroup(this.channelGroup)
                .setClientChannelMap(this.clientChannelMap)
                .setWsUri(WS_URI));

        /**
         * 心跳处理器
         */
        pipeline.addLast(heartbeatHandler);
    }

    public ChannelGroup getChannelGroup() {
        return channelGroup;
    }

    public WebSocketServerInitializer setChannelGroup(ChannelGroup channelGroup) {
        this.channelGroup = channelGroup;
        return this;
    }

    public ConcurrentHashMap<String, ChannelId> getClientChannelMap() {
        return clientChannelMap;
    }

    public WebSocketServerInitializer setClientChannelMap(ConcurrentHashMap<String, ChannelId> clientChannelMap) {
        this.clientChannelMap = clientChannelMap;
        return this;
    }
}